/*
 * Copyright (c) 2014 Strong Group - 版权所有
 * 
 * This software is the confidential and proprietary information of
 * Strong Group. You shall not disclose such confidential information 
 * and shall use it only in accordance with the terms of the license 
 * agreement you entered into with www.cnstrong.cn.
 */
package com.zuorenke.redis;

import redis.clients.jedis.*;
import redis.clients.jedis.params.SetParams;

import java.util.*;
import java.util.Map.Entry;

public class JedisClient extends AbstractCacheClient {
	//jedis连接池
	private JedisPool jedisPool;

	public void setJedisPool(JedisPool jedisPool) {
		this.jedisPool = jedisPool;
	}

	/**
	 *	
	 * 描述:从连接池中获取jedis连接
	 *
	 * @author  WQB
	 * @created 2014-5-20 下午4:29:01
	 * @since   v1.0.0 
	 * @return
	 * @return  Jedis
	 */
	public Jedis getResource() {
		return jedisPool.getResource();
	}

	/**
	 *	
	 * 描述:将jedis连接放回连接池中
	 *
	 * @author  WQB
	 * @created 2014-5-20 下午4:29:15
	 * @since   v1.0.0 
	 * @param resource
	 * @return  void
	 */
	public void returnResource(Jedis resource) {
		resource.close();
	}

    @Override
	public Boolean exists(String key) {
		Jedis jedis = getResource();
		try {
			return jedis.exists(key);
		} finally {
			returnResource(jedis);
		}
	}

    @Override
	public Long expire(String key, int seconds) {
		Jedis jedis = getResource();
		try {
			return jedis.expire(key, seconds);
		} finally {
			returnResource(jedis);
		}
	}

	@Override
	public Long ttl(String key) {
		Jedis jedis = getResource();
		try {
			return jedis.ttl(key);
		} finally {
			returnResource(jedis);
		}
	}

	@Override
	public Set<String> keys(String pattern) {
		Jedis jedis = getResource();
		try {
			return jedis.keys(pattern);
		} finally {
			returnResource(jedis);
		}
	}

    @Override
	public Boolean set(String key, byte[] value) {
		String result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.set(key.getBytes(), value);
		} finally {
			returnResource(jedis);
		}
		return "OK".equals(result);
	}

    @Override
	public Boolean set(String key, int seconds, byte[] value) {
		String result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.setex(key.getBytes(), seconds, value);
		} finally {
			returnResource(jedis);
		}
		return "OK".equals(result);
	}

    @Override
    public Boolean set(String key, String value, String nxxx, long time){
        String result = null;
        Jedis jedis = getResource();
        try {
	        SetParams params = new SetParams();
	        params.px(time);
	        if(nxxx.equals("XX")){
		        params.xx();
	        }else{
		        params.nx();
	        }
	        result = jedis.set(key, value, params);
        } finally {
            returnResource(jedis);
        }
        return "OK".equals(result);
    }

    @Override
    public Boolean eval(String script, List<String> keys, List<String> args){
        Object result = null;
        Long releaseSuccess = 1L;
        Jedis jedis = getResource();
        try {
            result = jedis.eval(script, keys, args);
        } finally {
            returnResource(jedis);
        }
        return releaseSuccess.equals(result);
    }

	public byte[] getBytes(String key) {
		byte[] result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.get(key.getBytes());
		} finally {
			returnResource(jedis);
		}
		return result;
	}

    @Override
	public Boolean delete(String key) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.del(key);
		} finally {
			returnResource(jedis);
		}

		return result == 1l;
	}

	@Override
	public List<Object> batchGet(final String[] keys) {
		Jedis jedis = getResource();
		List<Object> plList = new ArrayList<>();
		try {
			jedis.pipelined();
			for (String key : keys) {
				plList.add(jedis.get(key));
			}
			jedis.sync();
		} finally {
			returnResource(jedis);
		}

		return deserializePipelineResultForByteArray(plList);
	}

	@Override
	public List<Object> batchDelete(final String[] keys) {
		Jedis jedis = getResource();
		List<Object> plList = new ArrayList<>();
		try {
			jedis.pipelined();
			for (String key : keys) {
				plList.add(jedis.expire(key, 0));
			}
			jedis.sync();
		} finally {
			returnResource(jedis);
		}

		return plList;
	}

	@Override
	public List<Object> batchSets(final List<String> keys, final List<byte[]> values) {
		Jedis jedis = getResource();
		List<Object> plList = new ArrayList<>();
		try {
			jedis.pipelined();
			for (int i = 0; i < keys.size(); i++) {
				plList.add(jedis.set(keys.get(i).getBytes(), values.get(i)));
			}
			jedis.sync();
		} finally {
			returnResource(jedis);
		}
		return plList;
	}

	/**
	 *	
	 * 描述:序列化pipeline返回的byte[]的结果
	 *
	 * @author  WQB
	 * @created 2014-5-20 下午4:29:36
	 * @since   v1.0.0 
	 * @param list
	 * @return
	 * @return  List<Object>
	 */
	private List<Object> deserializePipelineResultForByteArray(List<Object> list) {
		List<Object> resultList = new ArrayList<>(list.size());
		for (Object object : list) {
			if (null == object) {
				continue;
			}
			Object o = getSerializer().deserialize((byte[]) object);
			resultList.add(o);
		}
		return resultList;
	}

	@Override
	public Long lpush(String key, byte[] value) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.lpush(key.getBytes(), value);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public List<byte[]> lrangeBytes(String key, int start, int end) {
		List<byte[]> result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.lrange(key.getBytes(), start, end);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Long llen(String key) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.llen(key);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public byte[] rpopBytes(String key) {
		byte[] result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.rpop(key.getBytes());
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public byte[] lpopBytes(String key) {
		byte[] result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.lpop(key.getBytes());
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Long decrBy(String key, long integer) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.decrBy(key, integer);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Long incr(String key) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.incr(key);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Long incrBy(String key, long integer) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.incrBy(key, integer);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Boolean hexists(String key, String field) {
		Boolean result = false;
		Jedis jedis = getResource();
		try {
			result = jedis.hexists(key, field);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Set<String> hkeys(String key) {
		Set<String> result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.hkeys(key);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Long hdel(String key, String field) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.hdel(key, field);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Long hsetBytes(String key, String field, byte[] value) {
		Long result = 0L;
		Jedis jedis = getResource();
		try {
			result = jedis.hset(key.getBytes(), field.getBytes(), value);
		} finally {
			returnResource(jedis);
		}
		return result;
	}
	
	@Override
	public String hmsetBytes(String key, Map<String, byte[]> map){
		Jedis jedis = getResource();
		String result = null;
		Map<byte[], byte[]> temp = new HashMap<>();
		for (Entry<String,byte[]> entry : map.entrySet()) {
			temp.put(entry.getKey().getBytes(), entry.getValue());
		}
		try {
			result = jedis.hmset(key.getBytes(), temp);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public byte[] hgetBytes(String key, String field) {
		byte[] result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.hget(key.getBytes(), field.getBytes());
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Long hincrBy(String key, String field, long integer) {
		Long result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.hincrBy(key, field, integer);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public List<byte[]> hmgetBytes(String key, String... fields) {
		byte[][] byteFields = new byte[fields.length][];
		for (int i = 0; i < fields.length; i++) {
			byteFields[i] = fields[i].getBytes();
		}
		List<byte[]> result = null;
		Jedis jedis = getResource();
		try {
			result = jedis.hmget(key.getBytes(), byteFields);
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public Map<byte[], byte[]> hgetAllBytes(String key) {
		Map<byte[], byte[]> result = null;
		Jedis jedis = getResource();

		try {
			result = jedis.hgetAll(key.getBytes());
		} finally {
			returnResource(jedis);
		}
		return result;
	}

	@Override
	public void publish(String channel, String message) {
		Jedis jedis = getResource();
		try {
			jedis.publish(channel, message);
		} finally {
			returnResource(jedis);
		}
	}

	@Override
	public void psubscribe(JedisPubSub jedisPubSub, String[] patterns) {
		Jedis jedis = getResource();
		try {
			jedis.psubscribe(jedisPubSub, patterns);
		} finally {
			returnResource(jedis);
		}
	}

	@Override
	public void flushAll() {
		Jedis jedis = getResource();
		jedis.flushAll();
	}

    @Override
    public Long sadd(String key, List<Object> values){
        byte[][] byteArr = new byte[values.size()][];
        for(int i = 0; i < values.size(); i++){
            byteArr[i] = getSerializer().serialize(values.get(i));
        }
        Long result = null;
        Jedis jedis =  getResource();
        try {
            result = jedis.sadd(key.getBytes(), byteArr);
        } finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long sadd(String key, Object value){
        Long result = null;
        Jedis jedis =  getResource();
        try {
            result = jedis.sadd(key.getBytes(), getSerializer().serialize(value));
        } finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public <T> Set<T> smembers(String key) {
        Set<T> result = new HashSet<>();
        Jedis jedis =  getResource();
        try {
            Set<byte[]> byteResult = jedis.smembers(key.getBytes());
            byteResult.forEach(r -> result.add((T) getSerializer().deserialize(r)));
        } finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Boolean sismember(String key, Object value) {
        Jedis jedis = getResource();
        Boolean result = false;
        try {
            result = jedis.sismember(key.getBytes(), getSerializer().serialize(value));
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long srem(String key, Object value) {
        Jedis jedis = getResource();
        Long result = null;
        try {
            result = jedis.srem(key.getBytes(), getSerializer().serialize(value));
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long srem(String key, List<Object> values) {
        byte[][] byteArr = new byte[values.size()][];
        for(int i = 0; i < values.size(); i++){
            byteArr[i] = getSerializer().serialize(values.get(i));
        }
        Jedis jedis = getResource();
        Long result = null;
        try {
            result = jedis.srem(key.getBytes(), byteArr);
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long scard(String key){
        Jedis jedis = getResource();
        Long result = null;
        try {
            result = jedis.scard(key.getBytes());
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long zadd(String key, double score, Object member){
        byte[] byteKey = key.getBytes();
        byte[] byteMember = getSerializer().serialize(member);
        Jedis jedis = getResource();
        Long result = null;
        try {
            result = jedis.zadd(byteKey, score, byteMember);
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long zadd(String key, Map<Object, Double> scoreMembers){
        byte[] byteKey = key.getBytes();
        Map<byte[], Double> byteScoreMembers = new HashMap<>(scoreMembers.size());
        scoreMembers.forEach((k, v) ->
            byteScoreMembers.put(getSerializer().serialize(k), v)
        );
        Jedis jedis = getResource();
        Long result = null;
        try {
            result = jedis.zadd(byteKey, byteScoreMembers);
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long zrem(String key, Object value){
        byte[] byteKey = key.getBytes();
        byte[] byteValue = getSerializer().serialize(value);
        Jedis jedis = getResource();
        Long result = null;
        try {
            result = jedis.zrem(byteKey, byteValue);
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public <T> List<T> zrange(String key, long start, long end, boolean reverse){
        byte[] byteKey = key.getBytes();
        List<T> result = new ArrayList<>();
        Jedis jedis = getResource();
        try{
            Set<byte[]> byteResult = reverse ? jedis.zrevrange(byteKey, start, end) : jedis.zrange(byteKey, start, end);
            byteResult.forEach(r ->
                result.add((T) getSerializer().deserialize(r))
            );
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public <T> Map<T, Double>zrangeWithScores(String key, long start, long end){
        byte[] byteKey = key.getBytes();
        Map<T, Double> result = new LinkedHashMap<>();
        Jedis jedis = getResource();
        try {
            Set<Tuple> tuples = jedis.zrangeWithScores(byteKey, start, end);
            tuples.stream().forEach(t ->
                result.put((T)getSerializer().deserialize(t.getBinaryElement()), t.getScore())
            );
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public <T> List<T> zrangeByScore(String key, double min, double max){
        byte[] byteKey = key.getBytes();
        List<T> result = new ArrayList<>();
        Jedis jedis = getResource();
        try{
            Set<byte[]> byteResult = jedis.zrangeByScore(byteKey, min, max);
            byteResult.forEach(r ->
                result.add((T) getSerializer().deserialize(r))
            );
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public <T> Map<T, Double> zrangeByScoreWithScores(String key, double min, double max){
        byte[] byteKey = key.getBytes();
        Map<T, Double> result = new LinkedHashMap<>();
        Jedis jedis = getResource();
        try{
            Set<Tuple> tuples = jedis.zrangeByScoreWithScores(byteKey, min, max);
            tuples.stream().forEach(t ->
                result.put((T)getSerializer().deserialize(t.getBinaryElement()), t.getScore())
            );
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long zcard(String key){
        byte[] byteKey = key.getBytes();
        Jedis jedis = getResource();
        Long result = null;
        try{
            result = jedis.zcard(byteKey);
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Double zscore(String key, Object member) {
        byte[] byteKey = key.getBytes();
        byte[] byteMember = getSerializer().serialize(member);
        Jedis jedis = getResource();
        Double result = null;
        try{
            result = jedis.zscore(byteKey, byteMember);
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long zcount(String key, double min, double max) {
        byte[] byteKey = key.getBytes();
        Jedis jedis = getResource();
        Long result = null;
        try{
            result = jedis.zcount(byteKey, min, max);
        }finally {
            returnResource(jedis);
        }
        return result;
    }

    @Override
    public Long zrank(String key, Object member, boolean reverse) {
        byte[] byteKey = key.getBytes();
        byte[] byteMember = getSerializer().serialize(member);
        Jedis jedis = getResource();
        Long result = null;
        try{
            result = reverse ? jedis.zrevrank(byteKey, byteMember) : jedis.zrank(byteKey, byteMember);
        }finally {
            returnResource(jedis);
        }
        return result;
    }
}
